package org.marverse.universe

import scala.collection.mutable.{ArrayBuffer, Stack}

class UniverseTransaction(private var _before: Universe) {
  
  def before = _before

  private class CompoundBuilder(val description: AnyRef, var after: Universe, beginErrornous: Boolean) {
    val transitions = new ArrayBuffer[UniverseTransition]()
    var errors = beginErrornous
  }
  
  private val stack = {
    val s = new Stack[CompoundBuilder]
    s.push(new CompoundBuilder(before, before.advance, false))
    s
  }
  
  def after: Universe = stack.top.after
  def hasErrors: Boolean = stack.top.errors
  
  def open(description: AnyRef) {
    stack.push(new CompoundBuilder(description, after.advance, hasErrors))
  }
  def append(transition: UniverseTransition) {
    stack.top.transitions.append(transition)
    if (transition.hasErrors)
      stack.top.errors = true
    if (!hasErrors)
      transition.perform(stack.top.after)
  }
  def close(propagateErrors: Boolean) {
    if (stack.size <= 1)
      throw new IllegalStateException("Inconsistent number of open/close in UniverseTransaction")
    if (!hasErrors) {
      // standard behavior
	  val compound = new CompoundTransition(stack.top.description, stack.top.transitions, false)
	  val newAfter = stack.pop.after
	  stack.top.after = newAfter
	  stack.top.transitions.append(compound)
    } else {
      // keep errors, don't advance universe 
	  val compound = new CompoundTransition(stack.top.description, stack.top.transitions filter { _.hasErrors }, true)
      stack.pop
      stack.top.transitions.append(compound)
      if (propagateErrors)
        stack.top.errors = true
    }
  }
  
  def finish: Seq[UniverseTransition] = {
    if (stack.size != 1)
      throw new IllegalStateException("Inconsistent number of open/close in UniverseTransaction")
    val result = Seq(stack.top.transitions :_*)
    stack.top.transitions.clear
    _before = stack.top.after
    stack.top.after = before.advance
    result
  }
  
}
